#include "hash.h"
#include "mm.h"

typedef struct hash_node_t
{
	void* m_data;
	struct hash_node_t* m_next;
}hash_node_t;

typedef struct hash_t
{
	int m_size;
	int m_count;

	hash_func m_hash_func;
	cmp_func m_cmp_func;
	hash_malloc m_malloc_func;
	hash_free m_free_func;

	struct hash_node_t** m_table;
}hash_t;

struct hash_t* hash_init(hash_func hash, cmp_func cmp, 
	hash_malloc malloc_func, hash_free free_func, int hint_size)
{
	if (!hash || !cmp || hint_size <= 0)
		return NULL;

	struct hash_t* htable;
	if(malloc_func)
		htable = (struct hash_t*)(malloc_func(sizeof(struct hash_t)));
	else
		htable = (struct hash_t*)(MALLOC(sizeof(struct hash_t)));
	assert(htable);
	
	memset(htable, 0, sizeof(*htable));
	htable->m_size = hint_size;
	htable->m_cmp_func = cmp;
	htable->m_hash_func = hash;
	
	if(malloc_func)
		htable->m_malloc_func = malloc_func;
	else
		htable->m_malloc_func = malloc;
	
	if(free_func)
		htable->m_free_func = free_func;
	else
		htable->m_free_func = free;
	
	htable->m_table = (hash_node_t**)htable->m_malloc_func(sizeof(hash_node_t*) * htable->m_size);
	assert(htable->m_table);
   	memset(htable->m_table, 0, sizeof(hash_node_t*) * htable->m_size);
	return htable;
}

int hash_release(struct hash_t* htable)
{
	if (!htable) return -1;

	hash_clean(htable);
	if (htable->m_table)
	{
		htable->m_free_func(htable->m_table);
		htable->m_table = NULL;
	}
	htable->m_free_func(htable);
	htable = NULL;
	return 0;
}

int hash_clean(struct hash_t* htable)
{
	if (!htable) return -1;

	int i = 0;
	for (; i < htable->m_size; i++)
	{
		// free list node
        		hash_node_t* node = htable->m_table[i];
		hash_node_t* bak = 0;
		while (node)
		{
			bak = node;
			node->m_data = 0;
			node = node->m_next;
			htable->m_free_func(bak);
			bak = 0;
		}
		htable->m_table[i] = 0;
	}

	return 0;
}

int hash_insert(struct hash_t* htable, void* data)
{
	if (!htable || !data) return -1;

	int hash_key = htable->m_hash_func(data);
	int index = hash_key % htable->m_size;
	hash_node_t* node = htable->m_table[index];
	hash_node_t* prev = 0;
	while (node)
	{
		// exist items
		if (0 == htable->m_cmp_func(node->m_data, data))
			return -1;
		prev = node;
		node = node->m_next;
	}

	node = (hash_node_t*)htable->m_malloc_func(sizeof(struct hash_node_t));
	node->m_data = data;
	node->m_next = 0;
	if (prev)
		prev->m_next = node;
	else
		htable->m_table[index] = node;

	htable->m_count ++;
	return 0;
}

int hash_remove(struct hash_t* htable, void* data)
{
	if (!htable || !data) return -1;

	int hash_key = htable->m_hash_func(data);
	int index = hash_key % htable->m_size;
	hash_node_t* node = htable->m_table[index];
	hash_node_t* prev = 0;

	while (node)
	{
		if (0 == htable->m_cmp_func(node->m_data, data))
		{
			if (prev)
				prev->m_next = node->m_next;
			else
				htable->m_table[index] = 0;
			htable->m_free_func(node);
			node = 0;
			htable->m_count --;
			return 0;
		}
		prev = node;
		node = node->m_next;
	}
	return -1;
}

int hash_count(struct hash_t* htable)
{
	if(!htable) return -1;
	return htable->m_count;
}


void* hash_find(struct hash_t* htable, void* data)
{
	if (!htable || !data) return NULL;

	int hash_key = htable->m_hash_func(data);
	struct hash_node_t* node = htable->m_table[hash_key % htable->m_size];

	while (node)
	{
		if (0 == htable->m_cmp_func(node->m_data, data))
			return node->m_data;
		node = node->m_next;
	}
	return NULL;
}

